module ActiveTest #:nodoc:

  class ActiveTestError < StandardError #:nodoc:
  end

  # ActiveTest::Base wraps the original Test::Unit::TestCase in a non-invasive manner.
  # 
  # The most significant way it achieves this is by using two class-inherited arrays,
  # one for your setup methods and one for your teardowns. This allows multiple setup
  # and teardown methods to be defined, even in the same class, which will be later
  # executed in the order of definition.
  #
  # Every class which is a kind of ActiveTest::Base will receive this behaviour for free.
  #
  # However, some may not wish to have this effect, and so it is perfectly possible
  # to remove all inheritance between ActiveTest::Base and your test case by using the
  # standalone_setup and standalone_teardown methods.
  #
  # *Note*: This will not affect additions by other libraries or gems which use
  # alias_method to use their custom setups.
  class Base < Test::Unit::TestCase

    class_inheritable_array :setup_methods
    class_inheritable_array :teardown_methods

    self.setup_methods = []
    self.teardown_methods = []

    # Execute all defined setup methods beyond Test::Unit::TestCase.
    def setup_with_nesting
      self.setup_methods.each { |method| method.bind(self).call }
    end
    alias_method :setup, :setup_with_nesting

    # Execute all defined teardown methods beyond Test::Unit::TestCase.
    def teardown_with_nesting
      self.teardown_methods.each { |method| method.bind(self).call }
    end
    alias_method :teardown, :teardown_with_nesting

    # Suck in every setup and teardown defined, unbind it, remove it and
    # execute it on the child. From here on out, we nest setup/teardown.
    def self.method_added(symbol)
      unless self == ActiveTest::Base
        case symbol.to_s
        when 'setup'
          self.setup_methods = [instance_method(:setup)]
          remove_method(:setup)
        when 'teardown'
          self.teardown_methods = [instance_method(:teardown)]
          remove_method(:teardown)
        end
      end
    end
    
    # If you do not want to inherit the setups of parents, use this class
    # method to force a single setup to execute.
    #
    # Example:
    #   class SpecialSituationControllerTest < ActiveTest::Controller
    #     standalone_setup do
    #       @controller = SpecialController.new
    #     end
    #   end
    def self.standalone_setup(&block)
      define_method(:standalone_setup, &block)
      @inheritable_attributes[:setup_methods] = [instance_method(:standalone_setup)]
      remove_method(:standalone_setup)
    end
    
    # If you do not want to inherit the teardown methods of parents, use
    # this class method to force a single teardown to execute. See
    # standalone_setup for an example.
    def self.standalone_teardown(&block)
      define_method(:standalone_teardown, &block)
      @inheritable_attributes[:teardown_methods] = [instance_method(:standalone_teardown)]
      remove_method(:standalone_teardown)
    end
  end
end